package com.example.config;

import com.example.auth.service.OaUserDetailService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;

import javax.sql.DataSource;

/**
 * @author X.I.O
 * @title: AuthorizationServerConfiguration
 * @projectName spring-cloud-1
 * @description: TODO
 * @date 2021/11/7 21:06
 */
@Configuration
@Slf4j
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

    @Autowired
    AuthenticationManager authenticationManager;

    @Autowired
    CustomAuthenticationProvider customAuthenticationProvider;

    private final DataSource dataSource;

    public AuthorizationServerConfiguration(DataSource dataSource) {
        this.dataSource = dataSource;
    }

//    @Autowired
//    RedisConnectionFactory redisConnectionFactory;


    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
       //从数据库获取客户端信息
        clients.jdbc(dataSource);
//        clients.inMemory().withClient("wuzzClientId")//客户端得ID,比如我们在QQ互联中心申请得。可以写多个。配置 循环
//                .secret(secret) // 客户端密钥，需要进行加密
//                .accessTokenValiditySeconds(7200)// token 有效时常  0 永久有效
//                .authorizedGrantTypes("password", "implicit", "refresh_token", "authorization_code")// 支持得授权类型
//                .redirectUris("http:127.0.0.1:8057")//回调地址
//                .scopes("all", "read", "write");//拥有的 scope  可选

//        clients.inMemory().withClient("wuzzClientId")//客户端得ID,比如我们在QQ互联中心申请得。可以写多个。配置 循环
//                .secret(passwordEncoder().encode("wuzzSecret")) // 客户端密钥，需要进行加密
//                .accessTokenValiditySeconds(7200)// token 有效时常  0 永久有效
//                .authorizedGrantTypes("password", "implicit", "refresh_token", "authorization_code")// 支持得授权类型
//                .redirectUris("http:127.0.0.1:8057")//回调地址
//                .scopes("all", "read", "write");//拥有的 scope  可选

//        String finalSecret = "{bcrypt}" + new BCryptPasswordEncoder().encode("123456");
//
//        // 配置两个客户端，一个用于password认证一个用于client认证
//        clients.inMemory().withClient("client_1")
//                .authorizedGrantTypes("client_credentials", "refresh_token")
//                .scopes("select")
//                .authorities("oauth2")
//                .secret(finalSecret)
//                .and().withClient("client_2")
//                .authorizedGrantTypes("password", "refresh_token")
//                .scopes("server")
//                .authorities("oauth2")
//                .secret(finalSecret);
    }

    @Autowired
    private CustomOAuth2ExceptionTranslator customOAuth2ExceptionTranslator;

    @Autowired
    private CustomTokenEnhancer customTokenEnhancer;

    @Autowired
    private TokenStore tokenStore;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {

        endpoints.userDetailsService(userDetailsService()) // 用户信息得服务，一版是都数据库
                .authenticationManager(authenticationManager())// 认证管理器。
                .exceptionTranslator(customOAuth2ExceptionTranslator) // 关键配置：注册自定义异常转换器
                .tokenStore(tokenStore)
                .tokenEnhancer(customTokenEnhancer) // 注册自定义Token增强器
                .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);


        //开启redis 存储 token
//        endpoints.tokenStore(new MyRedisTokenStore(redisConnectionFactory))
//                .authenticationManager(authenticationManager)
//                .allowedTokenEndpointRequestMethods(org.springframework.http.HttpMethod.GET, HttpMethod.POST);
    }



    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
//        // 允许表单认证
//        security.allowFormAuthenticationForClients();
        security.allowFormAuthenticationForClients()//允许表单登录
                .tokenKeyAccess("permitAll()")
                .checkTokenAccess("isAuthenticated()")
                .allowFormAuthenticationForClients()
                .checkTokenAccess("permitAll()"); //开启/oauth/check_token验证端口认证权限访问
    }

    @Bean// 注入认证管理器
    public AuthenticationManager authenticationManager() {
        AuthenticationManager authenticationManager = new CustomAuthenticationProvider(userDetailsService(),passwordEncoder(),daoAuthenticationProvider());
        return authenticationManager;
    }



    @Bean//注入认证器
    public AuthenticationProvider daoAuthenticationProvider() {
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        daoAuthenticationProvider.setUserDetailsService(userDetailsService());
        daoAuthenticationProvider.setHideUserNotFoundExceptions(false);
        daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
        return daoAuthenticationProvider;
    }

    @Bean//注入 用户信息服务
    public UserDetailsService userDetailsService() {
        return new OaUserDetailService();
    }


    @Bean//注入密码加密
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

}
